package cmpe451.group7.ourcity.recommendation;

import java.io.IOException;
import java.util.LinkedList;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.ParseException;

import cmpe451.group7.ourcity.entity.Tag;
import cmpe451.group7.ourcity.util.GlobalVariables;
import cmpe451.group7.ourcity.util.Utils;

import com.google.api.client.http.GenericUrl;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.PathNotFoundException;

/**
 * Sends the queries to the freebase system to autocomplete values used for
 * city, country and
 */
public class FreebaseQuery {

	public static final String SEARCH_COMPLETE_LIMIT = "10";

	/**
	 * Find a tag with the given query name
	 * 
	 * @param query
	 * @return
	 */
	public static Tag find(String query) {
		query = query.toLowerCase();
		List<Tag> similarTags = completeTags(query);

		// remove the last tag, since it doesn't belong to the freebase
		similarTags.remove(similarTags.size() - 1);

		for (Tag t : similarTags) {
			if (t.getTagName().toLowerCase().equals(query))
				return t;
		}
		return null;
	}

	/**
	 * Find tags with the given query
	 * 
	 * @param query
	 * @return
	 */
	public static List<Tag> completeTags(String query) {
		if (query.length() < 2)
			return null;
		try {
			JSONObject response = sendFreebaseQuery(query);
			JSONArray results = (JSONArray) response.get("result");
			List<Tag> completions = new LinkedList<Tag>();

			String name, notable, mid;
			for (Object result : results) {
				mid = JsonPath.read(result, "$.mid").toString();
				name = JsonPath.read(result, "$.name").toString();
				try {
					notable = JsonPath.read(result, "$.notable.name")
							.toString();
				} catch (PathNotFoundException e) {
					notable = null;
				}

				completions.add(new Tag(mid, name, notable));
			}

			// FIXME: don't enable user tag for now
			// completions.add(new Tag(null, query, null));

			return completions;
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ParseException e) {
			e.printStackTrace();
		}

		return null;
	}

	/**
	 * Used for auto completing country name
	 * 
	 * @param countryName
	 * @return
	 */
	public static List<String> completeCountry(String countryName) {
		return sendStringQuery(countryName, "filter",
				"(all type:/location/country)");
	}

	/**
	 * Used for auto completing city name
	 * 
	 * @param cityName
	 * @return
	 */
	public static List<String> completeCity(String cityName) {
		return sendStringQuery(cityName, "filter",
				"(all type:/location/citytown)");
	}

	/**
	 * Complete city
	 * 
	 * @param countryName
	 * @param cityName
	 * @return
	 */
	public static List<String> completeCity(String countryName, String cityName) {
		return sendStringQuery(
				cityName,
				"filter",
				String.format(
						"(all type:/location/citytown  /location/location/containedby:\"%s\")",
						countryName));
	}

	private static List<String> sendStringQuery(String query, String... filters) {
		if (query.length() < 2)
			return null;

		try {
			JSONObject response = sendFreebaseQuery(query, filters);
			JSONArray results = (JSONArray) response.get("result");
			List<String> completions = new LinkedList<String>();

			String name;
			for (Object result : results) {
				name = JsonPath.read(result, "$.name").toString();
				completions.add(name);
			}
			return completions;
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ParseException e) {
			e.printStackTrace();
		}

		return null;
	}

	/**
	 * Sends the search query to the Freebase
	 * 
	 * @param query
	 *            Query that is sent
	 * @param argPairs
	 *            other options to the query given as key/value pairs
	 * @return
	 */
	public static JSONObject sendGenericFreebaseQuery(String query,
			String limit, String... argPairs) throws IOException,
			ParseException {
		// prepare url
		GenericUrl url = new GenericUrl(
				"https://www.googleapis.com/freebase/v1/search");
		url.put("query", query);
		url.put("limit", limit);
		url.put("exact", "false");
		url.put("prefixed", "true");
		url.put("key", GlobalVariables.properties.getProperty("API_KEY"));
		int pairIndex = 0;

		while (pairIndex < argPairs.length) {
			// add the key value pairs to the query
			url.put(argPairs[pairIndex], argPairs[pairIndex + 1]);
			pairIndex += 2;
		}

		JSONObject response = Utils.sendWebServiceRequest(url);

		return response;
	}

	/**
	 * Helper function to send a Freebase query
	 * 
	 * @param query
	 * @param argPairs
	 * @return
	 * @throws IOException
	 * @throws ParseException
	 */
	public static JSONObject sendFreebaseQuery(String query, String... argPairs)
			throws IOException, ParseException {
		return sendGenericFreebaseQuery(query, SEARCH_COMPLETE_LIMIT, argPairs);
	}

	/**
	 * Find a Freebase object with the given MID
	 * 
	 * @param mid
	 * @return
	 * @throws IOException
	 * @throws ParseException
	 */
	public static JSONObject findByMID(String mid) throws IOException,
			ParseException {
		return FreebaseQuery.sendFreebaseQuery("", "mid", mid);
	}

	/**
	 * Find a tag with the given Freebase MID
	 * 
	 * @param mid
	 * @return
	 * @throws IOException
	 * @throws ParseException
	 */
	public static Tag findTagByMID(String mid) throws IOException,
			ParseException {
		JSONObject response = findByMID(mid);
		String status = JsonPath.read(response, "$.status");
		if (StringUtils.isNotEmpty(status)) {
			String tagName = JsonPath.read(response, "$.result[0].name");
			String notableName = JsonPath.read(response,
					"$.result[0].notable.name");

			return new Tag(mid, tagName, notableName);
		} else {
			return null;
		}
	}
}
